#include "Col.ph"
#ifdef __GNUG__
#pragma implementation
#endif

#include "ObjectTable.h"

#include "Class.h"
#include "OrdColl.h"
#include "Error.h"
#include "ClassManager.h"
#include "Math.h"
#include "File.h"
#include "ET_stdio.h"

ObjectTable *gObjectTable;

static bool rlock;
static int lasts;

//---- ObjectTable -------------------------------------------------------------

NewMetaImpl(ObjectTable,Object, (TVP(table,size), T(tally), TP(delayed)));

bool ObjectTable::died;
Object **ObjectTable::table;
int ObjectTable::size, ObjectTable::tally;
OrdCollection *ObjectTable::delayed, *ObjectTable::rdelayed;
int ObjectTable::iterlevel;
Class **ObjectTable::tab;

ObjectTable::ObjectTable()
{
	if (gObjectTable)
		Error("ObjectTable", "only one instance of ObjectTable");
	size= Math::NextPrime(10000);
	table= new Object* [size];
	tab= 0;
}

ObjectTable::~ObjectTable()
{
	SafeDelete(delayed);
	SafeDelete(rdelayed);
}

//---- static members --------------------------------------------------------

void ObjectTable::Add(Object *op)
{
	static bool olock= FALSE;

	if (op == 0) {
		::Error("ObjectTable::Add", "op is 0");
		return;
	}
	if (died || olock || op->TestFlag(cObjIsProto))
		return;
	if (gObjectTable == 0) {
		olock= TRUE;
		gObjectTable= new ObjectTable;
		olock= FALSE;
		Add(gObjectTable);
	}
	if (iterlevel > 0) {
		if (delayed == 0) {
			olock= TRUE;
			delayed= new OrdCollection;
			olock= FALSE;
		}
		delayed->Add(op);
	} else {
		table[FindElement(op)]= op;
		tally++;
		if (HighWaterMark())
			Expand(2 * size);
	}
}

void ObjectTable::Remove(Object *op)
{
	if (op == 0)
		::Error("ObjectTable::Remove", "remove 0 from ObjectTable");
	if (/* died || */ op->TestFlag(cObjIsProto) || rlock)
		return;

	Stream::InvalidatePtr(op);

	if (delayed && delayed->FindPtr(op)) {
		delayed->RemovePtr(op);
		return;
	}
	if (iterlevel > 0) {
		if (rdelayed == 0) {
			fprintf(stderr, "aha\n");
			rdelayed= new OrdCollection;
		}
		rdelayed->Add(op);
		return;
	}
	if (table == 0)
		return;
	int i= FindElement(op);
	if (table[i] == 0 && !op->TestFlag(cObjIsProto)) {
		::Warning("ObjectTable::Remove", "0x%08x not found at %d", (int)op, i);
		for (int j= 0; j < size; j++) {
			if (table[j] == op) {
				::Error("ObjectTable::Remove", "%d found at %d !!!", (int)op, j);
				i= j;
			}
		}
	}
	table[i]= 0;
	FixCollisions(i);
	tally--;

	if (gClassManager) {
		OrdCollection *col= gClassManager->GetClassTable();
		int s= col ? col->Size() : 0;
		
		if (tab == 0)
			tab= new Class* [s+2];
		if (tab[0] == 0) {
			if (s < lasts) {
				Iter next(col);
				register Class *cl;

				for (int i= 0; cl= (Class*) next(); i++)
					tab[i]= cl;
			}
			lasts= s;
		}
		if (s <= 0) {
			Object *t= gClassManager;
			gClassManager= 0;
			if (t)
				delete t;
			//Terminate();
		}
	}
}

void ObjectTable::Terminate()
{
	if (gObjectTable) {
		SafeDelete(gObjectTable);
		InstanceStatistics();
		died= TRUE;
		SafeDelete(table);
	}
}

int ObjectTable::FindElement(Object *op)
{
	register slot, n;
	register Object *slotOp;

	if (table == 0)
		return 0;
	slot= ((u_int) op >> 2) % size;
	for (n= 0; n < size; n++) {
		if ((slotOp= table[slot]) == 0)
			break;
		if (op == slotOp)
			break;
		if (++slot == size)
			slot= 0;
	}
	return slot;
}

void ObjectTable::FixCollisions (int index)
{
	register int oldIndex, nextIndex;
	register Object *nextObject;

	for (oldIndex= index+1; ;oldIndex++) {
		if (oldIndex >= size)
			oldIndex= 0;
		nextObject= table[oldIndex];
		if (nextObject == 0)
			break;
		nextIndex= FindElement(nextObject);
		if (nextIndex != oldIndex) {
			table[nextIndex]= nextObject;
			table[oldIndex]= 0;
		}
	}
}

void ObjectTable::Expand(int newSize)
{
	register int i;
	register Object **oldTable= table, *op;
	int oldsize= size;
	newSize= Math::NextPrime(newSize);
	table= new Object*[newSize];
	size= newSize;
	tally= 0;
	for (i= 0; i < oldsize; i++)
		if (op= oldTable[i])
			Add(op);
	delete oldTable;
}

void ObjectTable::InstanceStatistics()
{
	int i, n, cum= 0;

	if (tally == 0)
		return;
		
	if (tab)
		for (i= 0; tab[i]; i++)
			tab[i]->ResetInstanceCount();

	{
		register Object *op;
		ObjectTableIter next(0);
		while (op= next())
			if (op->TestFlag(cObjNonDeleted))
				op->IsA()->AddInstance();
			else
				fprintf(stderr, "oops 0x%08x\n", op);
	}

	fprintf(stderr, "\n");
	fprintf(stderr, "Object statistics\n");
	fprintf(stderr, "class                     cnt    size\n");
	fprintf(stderr, "=====================================\n");
	if (tab) {
		for (i= 0; tab[i]; i++) {
			n= tab[i]->GetInstanceCount();
			if (n > 0) {
				fprintf(stderr, "%-20s %8d%8d\n", tab[i]->Name(), n, tab[i]->Size());
				cum+= n;
			}
		}
		SafeDelete(tab);
	}
	fprintf(stderr, "-------------------------------------\n");
	fprintf(stderr, "Total                        %8d\n", cum);
	fprintf(stderr, "=====================================\n");
	fprintf(stderr, "\n");
}

void ObjectTable::UpdateInstCount(bool heaponly)
{
	register Object *op;

	if (table == 0)
		return;
		
	gClassManager->GetClassTable()->ForEach(Class,ResetInstanceCount)();
		
	ObjectTableIter next(0);
	if (heaponly) {
		while (op= next())
			if (op->IsShared())
				op->IsA()->AddInstance();
	} else {
		while (op= next())
			op->IsA()->AddInstance();
	}
}

void *ObjectTable::CheckPtrAndWarn(const char *msg, void *vp)
{
	if (table && vp && table[FindElement((Object*)vp)]) {
		Remove((Object*)vp);
		::Warning("ObjectTable::CheckPtrAndWarn", "%s (0x%08x)\n", msg, vp);
	}
	return vp;
}

Object *ObjectTable::SomeInstance(Class *cl)
{
	register Object *op;
	ObjectTableIter next(0);
	while (op= next())
		if (op->IsA()->isKindOf(cl))
			break;
	return op;
}

Object *ObjectTable::SomeMember(Class *cl)
{
	register Object *op;
	ObjectTableIter next(0);
	while (op= next())
		if (op->IsA() == cl)
			break;
	return op;
}

//---- ObjectTableIter ---------------------------------------------------------

ObjectTableIter::ObjectTableIter(Class *cl, bool mem)
{
	ObjectTable::iterlevel++;
	cursor= 0;
	fromClass= cl;
	members= mem;
}

ObjectTableIter::~ObjectTableIter()
{
	--ObjectTable::iterlevel;
	if (ObjectTable::iterlevel <= 0) {
		register Object *op;
		if (ObjectTable::delayed) {
			Iter next(ObjectTable::delayed);
			while (op= next())
				ObjectTable::Add(op);
			rlock= TRUE;
			SafeDelete(ObjectTable::delayed);
			rlock= FALSE;
		}
		if (ObjectTable::rdelayed) {
			Iter next(ObjectTable::rdelayed);
			while (op= next())
				ObjectTable::Remove(op);
			rlock= TRUE;
			SafeDelete(ObjectTable::rdelayed);
			rlock= FALSE;
		}
	}
}

Object *ObjectTableIter::operator()()
{
	if (ObjectTable::table == 0)
		return 0;
	register Object *op, **t= ObjectTable::table;
	int s= ObjectTable::size;

	for ( ; cursor < s ; cursor++) {
		if (op= t[cursor]) {
			if (fromClass == 0)
				break;
			if (members && fromClass == op->IsA())
				break;
			if (!members && op->IsA()->isKindOf(fromClass))
				break;
		}
	}
	return cursor < s ? t[cursor++] : 0;
}

void gIsA(Object *op)
{
	if (op == 0 || !ObjectTable::PtrIsValid(op))
		return;

	fprintf(stderr, "%s\n", op->ClassName());
	return;
}

